package osami.communication;


import java.io.IOException;
import java.util.Vector;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;

import osami.communication.translate.TranslationException;
//import osami.engine.OsamiEngine;
//import osami.engine.OsamiException;


/**
 * Bluetooth Ad Hoc device / service "getter" for listener using Serial Port Profile (SPP) bluetooth protocol. Listens for suitable
 * devices and searches if the device has OSAMI service. Gets the bluetooth address and sends it forward.
 */
public class BluetoothAdHoc implements DiscoveryListener, ConnectionListener
{
	private CommServer iCommServer;

	private static final int SLEEP_TIME = 1 * 60 * 1000;		// milliseconds
	
	private static Object iLock = new Object();
	private static Vector<RemoteDevice> iDevices = new Vector<RemoteDevice>();
	private static String iConnectionURL = null;
	private boolean iRunning;
	private Thread iListenerThread = null;

	/**
	 * Constructor for BluetoothAdHoc 
	 * @param aEngine
	 * @param aCommServer
	 * @throws BluetoothStateException
	 */
	public BluetoothAdHoc( Activator aEngine, CommServer aCommServer ) throws BluetoothStateException
	{
		iCommServer = aCommServer;
		LocalDevice.getLocalDevice();

		// create and start a thread in which the listener runs
		iListenerThread = new Thread( this );
		iListenerThread.start();
	}

	/**
	 * Main loop of the AdHoc
	 * 
	 */
	public void run()
	{
		// attributes are used to retrieve information about OSAMI node (service name and description).
		int[] attributes = {0x100,0x102};
		iRunning = true;

		LocalDevice localDevice = null;
		try
		{
			localDevice = LocalDevice.getLocalDevice();
		}
		catch ( BluetoothStateException e1 )
		{
			//Continue if local device is not found...
			return;
		}

		
		//Run the loop again and again, adjust this by thread sleep (we want to discover new devices periodically)
		while( iRunning )
		{
			System.out.println( "BluetoothAdHoc.run: Searching..." );

			DiscoveryAgent agent = localDevice.getDiscoveryAgent();
			try
			{
				//Search for bluetooth devices
				agent.startInquiry( DiscoveryAgent.GIAC, this );
			}
			catch ( BluetoothStateException e1 )
			{
				System.out.println("BluetoothAdHoc.run.exception in agent.inquiry!!");
				e1.printStackTrace();
			}

			try
			{
				synchronized( iLock )
				{
					iLock.wait();
				}
			}
			catch ( InterruptedException e )
			{
				System.out.println( "BluetoothAdHoc.run: interrupted." );
			}

			//System.out.println( "BT: Checking for OSAMI spp service..." );

			int index = 0;
			do
			{
				if ( index < iDevices.size() )	
				{
					
					RemoteDevice remoteDevice = (RemoteDevice)iDevices.elementAt( index );
					UUID[] uuidSet = new UUID[1];
					
					//Set UUID ("pin-code") to 1101 for SPP connection
					uuidSet[0] = new UUID( "1101", true ); //true = short int, false looooong

					try
					{
						//Go through devices and search for OSAMI -name in attributes
						agent.searchServices( attributes, uuidSet, remoteDevice, this );
					}
					catch ( BluetoothStateException e1 )
					{
						System.out.println("BluetoothAdHoc.run.exception in agent.searcServices!");
						e1.printStackTrace();
					}
	
					try
					{
						synchronized( iLock )
						{
							iLock.wait();
						}
					}
					catch ( InterruptedException e )
					{
						System.out.println( "BluetoothAdHoc.run: interrupted." );
					}
				}
		
				//	If device list has been gone through without identified Osami node, no OSAMI spp service found with this round.
				if ( index > iDevices.size() )
				{
					iConnectionURL = null;
					break;
				}

				index++;

				// connect
				try
				{
					if ( iConnectionURL != null )
					{
						BluetoothLongAddress address = new BluetoothLongAddress( iConnectionURL );
						iCommServer.connectTo( address );
					}
				}
				catch ( IOException exception )
				{
					exception.printStackTrace();
				}
				catch ( TranslationException exception )
				{
					exception.printStackTrace();
				}
				catch ( OsamiException exception )
				{
					exception.printStackTrace();
				}
			}
			while ( iConnectionURL == null || index < iDevices.size() - 1 );
		
			try
			{
				//One instance has completed, sleep for certain time and run device/osami service search again
				System.out.println( "BluetoothAdHoc.run: sleeping for " + ( SLEEP_TIME / 1000.0 ) + " seconds." );
				Thread.sleep( SLEEP_TIME );
			}
			catch ( InterruptedException exception )
			{
				System.out.println( "BluetoothAdHoc.run: interrupted." );
			}

			// clear the devices vector
			iDevices.clear();
		}
	}
	
	
	/**
	 * Adds the found bluetooth devices to the iDevices vector for further study.
	 */
	public void deviceDiscovered( RemoteDevice btDevice, DeviceClass cod )
	{
		//add the device to the vector
		if( !iDevices.contains( btDevice ) )
		{
			iDevices.addElement( btDevice );
		}
	}

	
	/** 
	 * Method to find out if the BT server/responsive client has OSAMI -property as a service name.
	 * If OSAMI node found then set connection url to this target device.
	 * @param transID 
	 * @param servRecord
	 */
	public void servicesDiscovered(int transID, ServiceRecord[] servRecord)
	{
		if( servRecord != null && servRecord.length > 0 )
		{
			if ( servRecord[0].getAttributeValue(0x102) != null )
			{
				//If server has a property, service name, set to OSAMI then the node shall be joined to the 'network'
				if ( servRecord[0].getAttributeValue(0x102).getValue().toString().contentEquals( "OSAMI" ) )
				{
				
					iConnectionURL = servRecord[0].getConnectionURL( 0, false );
					synchronized( iLock )
					{
						iLock.notify();
					}
				}
			}
			else 
			{
				//If bluetooth device supports spp -server but is not OSAMI node then do nothing, just look again..
				System.out.println("No OSAMI Bluetooth node found!");
			}
		}
	}

	/**
	 * Service search is complete, continue.
	 * @param transID
	 * @param respCode
	 */
	public void serviceSearchCompleted(int transID, int respCode)
	{
		synchronized( iLock )
		{
			iLock.notify();
		}
	}
	
	/**
	 * Current inquiry is complete, continue.
	 * @param discType
	 */
	public void inquiryCompleted(int discType)
	{
		synchronized( iLock )
		{
			iLock.notify();
		}
	}
	
	/**
	 * Closes the BluetoothAdHoc 'connection' by interrupting the thread.
	 */
	public void closeConnection()
	{
		iRunning = false;
		iListenerThread.interrupt();
	}
}